
/**
 * AppleIIGo
 * Memory class
 * (C) 2008 by Marc S. Ressl(mressl@umich.edu)
 * Released under the GPL
 */

#include <stdio.h>

#include "Memory.h"

// Error ROM
const UINT8 defaultBootRom[] = {
	0xad, 0x51, 0xc0, 0xa9, 0xa0, 0xa2, 0xff, 0x9d,
	0xff, 0x03, 0xca, 0xd0, 0xfa, 0xa2, 0xff, 0x9d,
	0xff, 0x04, 0xca, 0xd0, 0xfa, 0xa2, 0xff, 0x9d,
	0xff, 0x05, 0xca, 0xd0, 0xfa, 0xa2, 0xff, 0x9d,
	0xff, 0x06, 0xca, 0xd0, 0xfa, 0xa2, 0x27, 0xbd,
	0xb8, 0xfe, 0x9d, 0x80, 0x04, 0xca, 0x10, 0xf7,
	0xa2, 0x27, 0xbd, 0xe0, 0xfe, 0x9d, 0x00, 0x05,
	0xca, 0x10, 0xf7, 0xa2, 0x27, 0xbd, 0x08, 0xff,
	0x9d, 0x80, 0x05, 0xca, 0x10, 0xf7, 0xa2, 0x27,
	0xbd, 0x30, 0xff, 0x9d, 0x00, 0x06, 0xca, 0x10,
	0xf7, 0xa2, 0x27, 0xbd, 0x58, 0xff, 0x9d, 0x80,
	0x07, 0xca, 0x10, 0xf7, 0xa2, 0x27, 0xbd, 0x80,
	0xff, 0x9d, 0x28, 0x04, 0xca, 0x10, 0xf7, 0xa2,
	0x27, 0xbd, 0xa8, 0xff, 0x9d, 0x00, 0x07, 0xca,
	0x10, 0xf7, 0xa2, 0x27, 0xbd, 0xd0, 0xff, 0x9d,
	0x00, 0x07, 0xca, 0x10, 0xf7, 0x4c, 0xad, 0xfe,
	0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea,
	0xa0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa0,
	0xa0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
	0x20, 0x20, 0x20, 0x01, 0x10, 0x10, 0x0c, 0x05,
	0x09, 0x09, 0x07, 0x0f, 0x20, 0x03, 0x0f, 0x15,
	0x0c, 0x04, 0x0e, 0x27, 0x14, 0x20, 0x20, 0x20,
	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa0,
	0xa0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
	0x20, 0x20, 0x20, 0x06, 0x09, 0x0e, 0x04, 0x20,
	0x14, 0x08, 0x05, 0x20, 0x12, 0x0f, 0x0d, 0x20,
	0x09, 0x0d, 0x01, 0x07, 0x05, 0x20, 0x20, 0x20,
	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa0,
	0xa0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa0,
	0xa0, 0xd0, 0xcc, 0xc5, 0xc1, 0xd3, 0xc5, 0xa0,
	0xc3, 0xcf, 0xce, 0xd3, 0xd5, 0xcc, 0xd4, 0xa0,
	0xd4, 0xc8, 0xc5, 0xa0, 0xc4, 0xcf, 0xc3, 0xd5,
	0xcd, 0xc5, 0xce, 0xd4, 0xc1, 0xd4, 0xc9, 0xcf,
	0xce, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
	0xa0, 0xc6, 0xcf, 0xd2, 0xa0, 0xcd, 0xcf, 0xd2,
	0xc5, 0xa0, 0xc9, 0xce, 0xc6, 0xcf, 0xd2, 0xcd,
	0xc1, 0xd4, 0xc9, 0xcf, 0xce, 0xa0, 0xa0, 0xa0,
	0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
	0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
	0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
	0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
	0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
	0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
	0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
	0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
	0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
	0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
	0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
	0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
	0x00, 0x00, 0x30, 0xfe, 0x30, 0xfe, 0x30, 0xfe,
};

Memory::Memory(Motherboard * motherboard,
			   UINT32 mainMemoryType, UINT32 mainMemoryByteNum,
			   char * romImage, UINT32 romByteNum, UINT32 romBank1Index) :
Device(motherboard, "Memory") {
	memoryPoolByteNum = 0;
	memoryPool = 0;
	
	this->mainMemoryType = mainMemoryType;
	this->mainMemoryByteNum = mainMemoryByteNum;
	
	this->romImage = romImage;
	this->romByteNum = romByteNum;
	this->romBank1Index = romBank1Index;
}

Memory::~Memory() {
	if (memoryPool)
		delete memoryPool;
}

void Memory::onMemoryRequested() {
	mainMemoryOffset = requestMemory(mainMemoryByteNum);
	shadowMemoryOffset = requestMemory(SHADOWMEMORY_END);
	romOffset = requestMemory(romByteNum);
	
	MMU * mmu = motherboard->getMmu();
	mmu->setMainMemory(mainMemoryOffset, mainMemoryByteNum);
	mmu->setRom(romOffset, romOffset + romBank1Index,
				romByteNum);
	
	mmu->setShadowMemory(shadowMemoryOffset);
}

void Memory::onMemoryAllocated() {
	if (!loadRomImage(romOffset, romByteNum, romImage))
		loadDefaultBootRomImage();
}

void Memory::onResetAsserted(bool isRestart) {
	if (!isRestart)
		return;
	
	UINT8 * mainMemory = getMemory(mainMemoryOffset);
	UINT8 * shadowMemory = getMemory(shadowMemoryOffset);
	
	switch(mainMemoryType) {
		case MEMORYTYPE_II:
			for (UINT32 index = 0; index < mainMemoryByteNum; index++) {
				mainMemory[index] = (index & 0x2) ? 0x00 : 0xff;
				if (index < (2 * BANK_BYTENUM))
					shadowMemory[index] = (index & 0x2) ? 0x00 : 0xff;
			}
			break;
		case MEMORYTYPE_IIE:
			for (UINT32 index = 0; index < mainMemoryByteNum; index++) {
				mainMemory[index] = (index & 0x1000) ? 0x00 : 0xff;
				if (index < (2 * BANK_BYTENUM))
					shadowMemory[index] = (index & 0x1000) ? 0x00 : 0xff;
			}
			break;
		case MEMORYTYPE_IIC:
			for (UINT32 index = 0; index < mainMemoryByteNum; index++) {
				mainMemory[index] = 0;
				if (index < (2 * BANK_BYTENUM))
					shadowMemory[index] = 0;
			}			
			break;
		case MEMORYTYPE_IIGS:
			for (UINT32 index = 0; index < mainMemoryByteNum; index++) {
				mainMemory[index] = (index & 0x1) ? 0x00 : 0xff;
				if (index < (2 * BANK_BYTENUM))
					shadowMemory[index] = (index & 0x1) ? 0x00 : 0xff;
			}
			break;
	}
}

UINT32 Memory::requestMemory(UINT32 byteNum) {
	UINT32 memoryOffset = memoryPoolByteNum;
	memoryPoolByteNum += byteNum;
	return memoryOffset;
}

bool Memory::allocateMemory() {
	memoryPool = new UINT8[memoryPoolByteNum];
	return (memoryPool != 0);
}

UINT8 * Memory::getMemory(UINT32 offset) {
	return memoryPool + offset;
}

UINT32 Memory::getMainMemoryOffset(UINT32 offset) {
	if (offset > mainMemoryByteNum)
		return OFFSET_FLOATINGBUS;
	
	return mainMemoryOffset + offset;
}

UINT8 * Memory::getShadowMemory() {
	return getMemory(shadowMemoryOffset);
}

bool Memory::loadRomImage(UINT32 offset, UINT32 byteNum, char * romImage) {
	Resources * resources = motherboard->getResources(); 
	UINT32 resourceByteNum = resources->getResourceByteNum(romImage);
	if (!resourceByteNum)
		return false;
	if (resourceByteNum > byteNum)
		resourceByteNum = byteNum;
	
	UINT8 * romMemory = getMemory(offset + byteNum - resourceByteNum);
	return (resources->readResource(romMemory,
									resourceByteNum,
									romImage) == resourceByteNum);
}

void Memory::loadDefaultBootRomImage() {
	UINT8 * romMemory = getMemory(romOffset + romByteNum -
								  sizeof(defaultBootRom));
	for (UINT32 index = 0; index < sizeof(defaultBootRom); index++)
		romMemory[index] = defaultBootRom[index];
}
